package com.gframework.boot.mvc.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.filter.OncePerRequestFilter;

import com.gframework.boot.mvc.filter.CrossDomainAccessFilter.CrossDomainProperties;

/**
 * 跨域允许过滤器.
 * <p>
 * 使用此过滤器后，将会允许一切跨域请求，主要是为了开发接端方便。
 * <p>
 * 
 * @since 1.0.0
 * @author Ghwolf
 */
@EnableConfigurationProperties(CrossDomainProperties.class)
@ConditionalOnProperty(value = "gframework.mvc.filter.cross-domain-access.enabled",havingValue = "true")
@Order(Integer.MIN_VALUE + 1)
public class CrossDomainAccessFilter extends OncePerRequestFilter {

	@ConfigurationProperties("gframework.mvc.filter.cross-domain-access")
	static class CrossDomainProperties {
		/**
		 * 是否开启跨域支持，开启后所有请求都支持跨域.
		 * <p>默认false
		 */
		private boolean enabled = false;
		
		public boolean isEnabled() {
			return this.enabled;
		}

		public void setEnabled(boolean enabled) {
			this.enabled = enabled;
		}
	}
	

	public CrossDomainAccessFilter() {
		logger.info("====> 已启用 CrossDomainAccessFilter 允许跨域过滤器！");
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		
		String origin = request.getHeader("origin");
		String method = request.getHeader("Access-Control-Request-Method");
		String header = request.getHeader("Access-Control-Request-Headers");

		response.setHeader("Access-Control-Allow-Headers", isBlank(header) ? "*" : header);
		response.setHeader("Access-Control-Allow-Methods", isBlank(method) ? "*" : method);
		response.setHeader("Access-Control-Allow-Origin", isBlank(origin) || "null".equals(origin) ? "*" : origin);
		response.setHeader("access-control-allow-credentials", "true");
		
		// 大部分浏览器默认策略是no-referrer-when-downgrade，但是2020年7月左右，chrome85版本开始将默认策略改为strict-origin-when-cross-origin，
		// 这会导致开发截断跨域请求无法读取到完整的referer
		// 也可以在标签设置：<meta name="referrer" content="no-referrer"/>
		// 不过要知道，这个响应头只能作用于返回的页面上，ajax请求返回是无效的
		response.setHeader("Referrer-Policy", "unsafe-url");
		
		if (request.getMethod().equalsIgnoreCase(RequestMethod.OPTIONS.name()) &&
					(
						StringUtils.isNotEmpty(request.getHeader("Access-Control-Request-Method")) ||
						StringUtils.isNotEmpty(request.getHeader("Access-Control-Request-Headers"))
					)
				) {
			return ;
		}
		
		filterChain.doFilter(request, response);
	}

	private boolean isBlank(String str) {
		int strLen;
		if (str == null || (strLen = str.length()) == 0) {
			return true;
		}
		for (int i = 0; i < strLen; i ++) {
			if ((Character.isWhitespace(str.charAt(i)) == false)) {
				return false;
			}
		}
		return true;
	}

}
